﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using Core.Module.Interfaces.Model;
using umbraco;
using System.Xml;
using Core.Module.Interfaces.Controller;
using Core.Module.Interfaces;
using Module.MailingSystem.Interfaces;
using umbraco.interfaces;
using umbraco.BusinessLogic;
using Core.Module;
using System.Net.Mail;
using System.Text.RegularExpressions;
using umbraco.presentation;
using System.Web.UI;
using Module.MultiSiteFields.Interfaces;


namespace Module.MailingSystem
{
	public class MailingSystemModule : IMailingModule
	{
		#region main properties
		
		public string Id
		{
			get { return "7949A490-297C-4B84-B7AF-1B226F18F5D7"; }
		}
		public string Alias
		{
			get { return "MailingModule"; }
		}
		public string Title
		{
			get { return ui.Text("multisite", "Module_" + Alias); }
		}
		public bool Enabled { get; set; }
		public bool Active { get; set; }
		[System.ComponentModel.DefaultValue(2)]
		public int Position {get; set; }
		public XmlElement Settings { get; set; }
		public ISite Site { get; set; }
		#endregion

        public void Init(ISite site)
        {
            Site = site;
            NodeController = ObjectManager.Resolve<INodeController<IFrontend>>();
            MailTemplates = NodeController.GetPropertyAsNode(site.Settings, "mailTemplatesId");
            if (MailTemplates == null && site.Settings!=null) MailTemplates = NodeController.GetChild(site.Settings.Id, "MailTemplates");
            if (MailTemplates == null){
                Log.Add(LogTypes.Notify, int.Parse(site.Node.Id), "Site:" + site.Node.Name + " hasn't any mail settings node! under site setting have to be node with alias [MailTemplates], or site settings has to have property with name [mailTemplatesId]");
            } else {
                SiteReplacements = GetSiteReplacements();
                UrlPrefix = GetUrlPrefix(System.Web.HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority).TrimEnd('/'));
                RegisterTreeStartPoints();
            }
        }       

        #region internal properties
        private IModuleNode MailTemplates { get; set; }
        private INodeController<IFrontend> NodeController { get; set; }
        private Dictionary<string, string> SiteReplacements { get; set; }
		private string UrlPrefix;
        #endregion
        
        public IModuleNode GetTemplateNode(IModuleNode forPage, string templateFieldOrNodeName)
		{
            if (forPage == null || forPage.SiteId != Site.Node.Id) return null;
            IModuleNode templateNode = NodeController.GetPropertyAsNode(forPage, templateFieldOrNodeName);
			if (templateNode != null) return templateNode;
			//find first template in config section
            if (MailTemplates == null) return LogErrorAndReturn<IModuleNode>("Site: " + forPage.SiteId + " hasn't [MailTemplates] as part of the [settings]", LogTypes.Notify, null);
			templateNode= NodeController.GetChildren(MailTemplates.Id).Where(mn=>mn.Name.Equals(templateFieldOrNodeName,StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
            if (templateNode == null) templateNode = NodeController.GetChild(MailTemplates.Id, forPage.Alias);

            return templateNode != null ? templateNode : LogErrorAndReturn<IModuleNode>("Site: " + Site.Node.Name + "[id:" + Site.Node.Id + "] hasn't any template with name[" + templateFieldOrNodeName + "] or with name[" + forPage.Alias + "]", LogTypes.Notify, null);
		}

        public IMailTemplate ReplaceTemplateParamethers(IModuleNode forPage, IMailTemplate templateMessage)
        {
            ExtendMessageParams(forPage, templateMessage);
            ReplaceMessageParams(forPage, templateMessage);
            return templateMessage;
        }
        
        public bool Send(IModuleNode forPage, IMailTemplate template)
        {
            if (template == null) return false;
            MailMessage message=Convert(forPage, template);
            if (!Enabled || message.To == null || message.To.Count < 1) return LogErrorAndReturn<bool>("The message doesn't send!Module is disable for site:" + Site.Node.Name + ", or message recipient is empty!", LogTypes.Notify, true);
            SmtpClient client=new SmtpClient();
            if (!string.IsNullOrEmpty(template.ServerName)) client.Host = template.ServerName;
            try
            {
                client.Send(message);
                return true;
            }
            catch (Exception ex)
            {
                return LogErrorAndReturn<bool>("Can't send message by template:"+template.Node.Name+", for page:"+forPage.Name+"[id:"+forPage.Id+"."+ex.Message+"\r\n"+ex.StackTrace , LogTypes.Error, false);
            }
        }

        public MailMessage Convert(IModuleNode forPage, IMailTemplate template)
        {
            MailMessage m = new MailMessage();
            m.From = GenerateMailAddress(template.From);
            GenerateMailAddressCollection(m.To, template.To);
            m.Subject = template.Subject;
            MailPriority prior = MailPriority.Normal;
            Enum.TryParse<MailPriority>(template.Priority, out prior);
            m.Priority = prior;
            GenerateMailAddressCollection(m.CC, template.CC);
            GenerateMailAddressCollection(m.Bcc, template.BCC);
            DeliveryNotificationOptions dno = DeliveryNotificationOptions.None;
            Enum.TryParse<DeliveryNotificationOptions>(template.DeliveryNotificationOptions, out dno);
            m.DeliveryNotificationOptions = dno;
            m.IsBodyHtml = (!string.IsNullOrEmpty(template.IsPlainText)) && template.IsPlainText == "1" ? false : true;
            if (!string.IsNullOrEmpty(template.Head) && m.IsBodyHtml)
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendFormat("<html><head>{0}</head><body>{1}</body></html>", template.Head, template.Template);
                m.Body = sb.ToString();
            }
            else m.Body = template.Template;
            return m;
        }
        
        #region Internal methods
        
        private Dictionary<string, string> GetSiteReplacements()
        {
            Dictionary<string, string> result = new Dictionary<string, string>();
            if (Settings == null) return result;
            foreach (INode item in Settings.SelectNodes("replacements/*"))
            {
                XmlElement ritem = item as XmlElement;
                if (ritem != null) result.Add(ritem.Name, ritem.Value);
            }
            return result;
        }

        private MailAddress GenerateMailAddress(string address)
        {
            return string.IsNullOrEmpty((address ?? "").Trim()) ? null : new MailAddress(address);
        }
        private void GenerateMailAddressCollection(MailAddressCollection mailAddressCollection, string address)
        {
            string[] ss = address.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
            foreach (string s in ss)
                mailAddressCollection.Add(GenerateMailAddress(s));
        }

        private string ConvertToFullUrl(IModuleNode page)
        {
            string url = page.Url;
            string res = string.IsNullOrWhiteSpace(url) ? url :
                url.StartsWith("/") ? string.Concat(UrlPrefix, url) : url;

            return res;
        }


        private T LogErrorAndReturn<T>(string message, LogTypes ltype, T result)
        {
            Log.Add(ltype, -1, message);
            return result;
        }

        private string GetUrlPrefix(string defaultValue)
        {
            if (Settings == null) return defaultValue;
            XmlElement staticPrefix = Settings.SelectSingleNode("prefix") as XmlElement;
            if (staticPrefix == null || string.IsNullOrEmpty(staticPrefix.Value)) return defaultValue;
            return staticPrefix.Value.Trim();
        }

        private void ExtendMessageParams(IModuleNode contentPage,IMailTemplate mailTemplate)
        {
            if (contentPage == null) return ;
            foreach (KeyValuePair<string, string> item in Context.Frontend.Nodes.GetProperties(contentPage))
                mailTemplate.Properties.Add("page_" + item.Key, item.Value);

            mailTemplate.Properties.Add("page_Id", contentPage.Id);
            mailTemplate.Properties.Add("page_Name", contentPage.Name);
            mailTemplate.Properties.Add("page_Url", ConvertToFullUrl(contentPage));
            mailTemplate.Properties.Add("page_Alias", contentPage.Alias);
            mailTemplate.Properties.Add("page_Path", contentPage.Path);
            mailTemplate.Properties.Add("page_SiteId", contentPage.SiteId);

            if (Site != null)
            {
                mailTemplate.Properties.Add("site_Id", Site.Node.Id);
                mailTemplate.Properties.Add("site_Title", Site.Node.Title);
                mailTemplate.Properties.Add("site_Name", Site.Node.Name);
                mailTemplate.Properties.Add("site_Alias", Site.Node.Alias);
                mailTemplate.Properties.Add("site_Url", ConvertToFullUrl(Site.Node));
                mailTemplate.Properties.Add("site_HomePage", ConvertToFullUrl(Site.Homepage));
                mailTemplate.Properties.Add("site_Path", Site.Node.Path);
            };
            foreach (var item in SiteReplacements)
                mailTemplate.Properties.Add(item.Key, item.Value);            
        }
               
        private void ReplaceMessageParams(IModuleNode forPage, IMailTemplate templateMessage)
        {
            string[] keys=templateMessage.Properties.Keys.ToArray();
            foreach (var k in keys)
            {
                string oldvalue = templateMessage.Properties[k];
                string newvalue = ReplaceTemplateProperty(templateMessage, oldvalue, forPage);
                if (!newvalue.Equals(oldvalue)) templateMessage.Properties[k] = newvalue;
            }
            if (!templateMessage.Properties.ContainsKey("showallparams")) templateMessage.Properties.Add("showallparams", GenerateTestMessage(forPage, templateMessage));
            templateMessage.From = ReplaceTemplateProperty(templateMessage, templateMessage.From, forPage);
            templateMessage.CC = ReplaceTemplateProperty(templateMessage, templateMessage.CC, forPage);
            templateMessage.BCC = ReplaceTemplateProperty(templateMessage, templateMessage.BCC, forPage);
            templateMessage.To = ReplaceTemplateProperty(templateMessage, templateMessage.To, forPage);

            //templateMessage.Title = ReplaceTemplateProperty(templateMessage, templateMessage.Title, forPage);

            if (!string.IsNullOrEmpty(templateMessage.Template))
                templateMessage.Template = ReplaceTemplateProperty(templateMessage, templateMessage.Template, forPage);
            else templateMessage.Template = templateMessage.Properties["showallparams"];
            templateMessage.Template = ReplaceTemplateUrl(templateMessage.Template);
            templateMessage.Subject = ReplaceTemplateProperty(templateMessage, templateMessage.Subject, forPage);
            templateMessage.Head = ReplaceTemplateProperty(templateMessage, templateMessage.Head, forPage);

        }

        private string ReplaceTemplateUrl(string textWithShortUrls)
        {
            StringBuilder sb = new StringBuilder(textWithShortUrls);
            Regex rx = new Regex(" src=\"([^\"]*)\"");
            MatchCollection matches = rx.Matches(textWithShortUrls ?? "");
            //foreach (Match item in matches )
            //{
                
            //}
            ///todo: here we could find: links, images etc and transform their urls to full format if they are short
            return sb.ToString(); 
        }

        private string GenerateTestMessage(IModuleNode forPage, IMailTemplate templateMessage)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("<p><h1>Hi!</h1></p>\r\n<p><b>This message has been generated automatically, to provide a list of template replacements.</b></p>");
            sb.AppendLine("<div>(it has happened because [template] field is empty or contains at list one time specified replacement: %%showallparams%%)</div>");
            sb.AppendLine("<p>Template for the contact page is different then template of the news brief page.<br/>");
            sb.AppendLine("list of replacements is different per functionality.</p>");
            sb.AppendLine("<p>below you can find list of replacements to build template of this page\\functionality");
            sb.AppendLine("Current page\functionality contains following list of replacements:</p><hr/>");
            sb.AppendLine("<table><tbody>");
            Int32 i = 0;
            foreach (var item in templateMessage.Properties)
            {
                i++;
                sb.AppendFormat("<tr><td>{2}.</td><td>Replacement:</td><td>%%{0}%% </td><td> current&nbsp;value:</td><td> {1}</td></tr>\r\n", item.Key, item.Value, i);
            }
            sb.AppendLine("</tbody></table>");
            sb.AppendLine("<hr/>");
            sb.AppendLine("<br/><p>You can use this text as template of the message. Just change it according to design.</p>");
            sb.AppendLine("<br/><p>Following fields could contains replacements:</p>");
            sb.AppendLine("<br/><p><b>1. [From]:</b> to dynamically pickup email address from which mail will be send.(contact email, visitor email etc)");
            sb.AppendLine("<br/><b>2. [To]:</b> to dynamically setup email address to which mail will be send.(contact email, visitor email etc)");
            sb.AppendLine("<br/><b>3. [CC,BCC]:</b> to dynamically setup email addresses to which mail will be send.(contact email, visitor email etc)");
            sb.AppendLine("<br/><b>3. [Title]:</b> to dynamically setup template title");
            sb.AppendLine("<br/><b>4. [Subject]:</b> to dynamically setup mail subject. by get data from page fields or form fields(by replacements)<p>");
            sb.AppendLine("-------------------------");
            sb.AppendLine("<p>Please replace data of the template:" + templateMessage.Node.Id + "; url" + templateMessage.Node.Url + ";site:" + Site.Node.Name);
            sb.AppendLine("<br/>(to have more better message then this)</p>");
            sb.AppendLine("<p>Regards.</p>");
            sb.AppendLine("");
            return sb.ToString();
        }

        private string ReplaceTemplateProperty(IMailTemplate template, string curData, IModuleNode forPage)
        {
            Regex rx = new Regex("%%([^%%]*)%%");
            MatchCollection matches = rx.Matches(curData ?? "");
            StringBuilder result = new StringBuilder(curData);
            foreach (Match match in matches)
                if (match.Groups.Count > 1)
                {
                    string key = match.Groups[1].Value;
                    string resvalue;
                    if (!template.Properties.TryGetValue(key, out resvalue))
                    {
                        resvalue = "##nofound:" + key + "##";
                        Log.Add(LogTypes.Error, int.Parse(forPage.Id), "SendMailingWrongKey(in method:ReplaceTemplateProperty, mailgenerator):" + key + ",pid:" + forPage.Id + ",tid:" + template.Node.Id.ToString() + ",purl:" + forPage.Url + ",t:" + template.Node.Url);
                    }
                    result.Replace("%%" + key + "%%", resvalue);
                }
            return result.ToString();
        }


        private void RegisterTreeStartPoints()
        {
            IStartPointsController startPoints = ObjectManager.Resolve<IStartPointsController>();
            startPoints.Register("content", "Site.MailTemplates",( point,forNode) =>
            {
                if (forNode == null || !Site.Node.Id.Equals(forNode.SiteId)) return false;
                startPoints.SetValue(point, "StartFrom", MailTemplates.Id);
                return true;
            });
        }
        #endregion
    }
}